import ctypes
import os, sys
sys.path.append(
    os.path.abspath(
        os.path.join(
            os.path.dirname(__file__), "../../")))
from lib3ddevil1.bindings import libc
del os, sys

#--------------------------------------+
#       Basic Struct
#--------------------------------------+

class TexturePack(ctypes.Structure):
    _pack_   = 1
    _fields_ = [
        ("id",               ctypes.c_char * 4), # fixed length 4, reverse order
        ("batchNumber",      ctypes.c_int),
        ("firstBatchOffset", ctypes.c_int),
        ("unknownA",         ctypes.c_int)
    ]

class TextureBatchDescriptor(ctypes.Structure):
    _pack_   = 1
    _fields_ = [
        ("batchIdx",    ctypes.c_int),
        ("hash",        ctypes.c_int),
        ("texNumber",   ctypes.c_int),
        ("unknownA",    ctypes.c_int * 8),
        ("textureSize", ctypes.c_int),
        ("unknownB",    ctypes.c_int * 30)
    ]

class Texture(ctypes.Structure):
    _pack_   = 1
    _fields_ = [
        ("data", ctypes.POINTER(ctypes.c_ubyte))
    ]

class TextureBatch(ctypes.Structure):
    _pack_   = 1
    _fields_ = [
        ("batch", ctypes.POINTER(Texture))
    ]

class Devil1TEX_FN(ctypes.Structure):
    _fields_ = [
        ("printheader",     ctypes.CFUNCTYPE(
                                 None, 
                                 ctypes.POINTER(TexturePack))),
        ("printbatchdesc",  ctypes.CFUNCTYPE(
                                 None,
                                 ctypes.POINTER(TextureBatchDescriptor))),
        ("getheader",     ctypes.CFUNCTYPE(
                                 ctypes.c_bool, 
                                 ctypes.POINTER(
                                    ctypes.POINTER(TexturePack)),
                                 ctypes.c_char_p)),
        ("getbatchdesc",    ctypes.CFUNCTYPE(
                                 ctypes.c_bool,
                                 ctypes.POINTER(
                                    ctypes.POINTER(TextureBatchDescriptor)),
                                 ctypes.c_uint,
                                 ctypes.c_char_p,
                                 ctypes.c_uint)),
        ("getbatch",        ctypes.CFUNCTYPE(
                                 ctypes.c_bool,
                                 ctypes.POINTER(
                                    ctypes.POINTER(TextureBatch)),
                                 ctypes.c_uint,
                                 ctypes.c_char_p,
                                 ctypes.c_uint)),
        ("gettextures",     ctypes.CFUNCTYPE(
                                 ctypes.c_bool,
                                 ctypes.POINTER(Texture),
                                 ctypes.c_uint,
                                 ctypes.c_char_p,
                                 ctypes.c_uint))
    ]

devil1tex = Devil1TEX_FN.in_dll(libc, "DEVIL1TEX")
del libc

#--------------------------------------+
#       Pythonic Object
#--------------------------------------+

class pyTexturePack:
    def __init__(self, filedata):
        self.cstruct = ctypes.pointer(TexturePack())
        if not devil1tex.getheader(ctypes.byref(self.cstruct), filedata):
            raise RuntimeError("failed to get texture pack header")
        return

    def show(self):
        devil1tex.printheader(self.cstruct)

    def getbatchnumber(self):
        return self.cstruct.contents.batchNumber

    def getfirstbatchoffset(self):
        return self.cstruct.contents.firstBatchOffset

class pyTextureBatchDescriptor:
    def __init__(self, i, filedata):
        self.cstruct = ctypes.pointer(TextureBatchDescriptor())
        ptrofptr = ctypes.byref(self.cstruct)
        if filedata:
            if not devil1tex.getbatchdesc(
                    ptrofptr, 
                    i, 
                    filedata, 
                    len(filedata)):
                raise RuntimeError("failed to get texturebatchdescriptor #" + str(i))
        return

    def show(self):
        devil1tex.printbatchdesc(self.cstruct)

    def getbatchidx(self):
        return self.cstruct.contents.batchIdx

    def gethash(self):
        return self.cstruct.contents.hash

    def gettexnumber(self):
        return self.cstruct.contents.texNumber

    def gettexturesize(self):
        return self.cstruct.contents.textureSize

class pyTextureBatch:
    def __init__(self, i, filedata):
        self.cstruct = TextureBatch()
        if filedata:
            self.cstruct.batch = None
            tbd = pyTextureBatchDescriptor(i, filedata)
            self.amount = tbd.gettexnumber()
            self.size   = tbd.gettexturesize()
            memsize     = self.amount * self.size
            self.cstruct.batch = ctypes.cast(
                             ctypes.create_string_buffer(memsize), 
                             ctypes.POINTER(Texture))
            if not devil1tex.gettextures(
                    self.cstruct.batch, 
                    i, 
                    filedata, 
                    len(filedata)):
                raise RuntimeError("failed to get textures of batch #" + str(i))
        return

    def gettextures(self):
        ptrs = self.cstruct.batch[:self.amount]
        textures = []
        for ptr in ptrs:
            texture = ctypes.cast(ptr.data,
                                  ctypes.POINTER(
                                    ctypes.c_ubyte * self.size))[0]
            textures.append(texture)
        return textures

